#!/bin/bash
#
# Traversal all cases in specified dir and format output the testlist
# bkr-runtest can parse the testlist, and submit them to beaker
#
# Author: jiyin@redhat.com

parse_subtest() {
	local ys="$*"
	echo "$ys" | awk 'BEGIN {FS=""; P=0; S=0}
	{
		for(n=1;n<=NF;n++) {
			if ($n==" " && $(n-1)==" " && P==0 && S==0)
				continue
			if ($n=="," && P==0 && S==0) {
				print gensub("^ ", "", 1, s); s=""
			} else {
				s=s$n;
				if(S==0 && $n=="\"" && $(n-1)!="\\")
					P=(P+1)%2;
				if(P==0 && $n=="'"'"'")
					S=(S+1)%2
			}
		}
	}
	END {print gensub("^ ", "", 1, s)}'
}

ymlhash() {
	local k=$1 v=$2
	[[ -z "$k" ]] && return
	v=${v:-?}

	[[ $k =~ [:,[] ]] && k="'$k'"
	[[ $v =~ [:,[] ]] && v="'$v'"
	echo -n "${k}: $v, "
}
ymllist() {
	local e=$1
	[[ -z "$e" ]] && return

	[[ $e =~ [:,] ]] && e="'$e'"
	echo -n "$e, "
}

_caseInfo() {
	[ $# -lt 1 ] && {
		echo "Usage: func <case path>" >&2
		return 1
	}

	local f=runtest.sh
	local infofile=testinfo.desc
	local casepath=$1
	local name=

	declare -A Attr
	local _time= _pkg= _ssched=no _type= _level=

	declare -a Setup
	local topo=

	[ -d "$casepath" ] || { echo "{warn} dir [$casepath] not exist." >&2; return; }

	pushd $casepath >/dev/null;

	unlink $infofile 2>/dev/null
	make $infofile &>/dev/null
	[ -f $infofile ] || {
		echo "{ERROR} [$casepath/] make $infofile fail, please fix the Makefile." >&2
		popd >/dev/null; return 1
	}

	#get case name
	name=$(awk -F'[: =\t]+' '/^Name:/{print $2}' $infofile)
	[ -z "$name" ] && {
		echo "{ERROR} [$casepath/] casename NULL, maybe Makefile wrong." >&2
		popd >/dev/null; return 1
	}

	#get test attr: max run time
	_time=$(awk -F'[: =\t]+' '/^TestTime:/{print $2}' $infofile)
	echo $_time | egrep -q '[0-9]+h' && _time=$((${_time/h/}*60))m
	echo $_time | egrep -q '[0-9]+d' && _time=$((${_time/d/}*24*60))m
	Attr[time]=$_time

	#get test attr: pkg name belong to
	unset PACKAGE_NAME PACKAGE
	eval "$(egrep '^[[:space:]]*(export *)?PACKAGE(_NAME)?=[^ #]' -h * .* 2>/dev/null|sed 's;export;;')"
	_pkg=${PACKAGE_NAME}
	test -z "${_pkg}" && _pkg=${PACKAGE}
	test -z "${_pkg}" && _pkg=Function
	Attr[pkg]=$_pkg

	#get test attr: sepatate scheduling?
	egrep -q -i ' *DANGEROU?S=yes' -r . && _ssched="yes.dangerous"
	egrep -q -i ' *STRESS=yes' -r . && _ssched="yes.stress"
	#
	## if cost so many time separate schedule too
	echo $maxtime | egrep -q '[0-9]+h' && maxtime=$((${maxtime/h/}*60))
	echo $maxtime | egrep -q '[0-9]+d' && maxtime=$((${maxtime/d/}*24*60))
	maxtime=${maxtime//[^0-9]/}
	[ "$_ssched" = "no" -a "${_time/m/}" -gt "${maxtime:-120}" ] && _ssched="yes.longtime"
	Attr[ssched]=$_ssched

	#get test attr: type info
	_type=$(awk '{match($0,"Type:[ \t]*.*(.egression|.tress|.unction|.anity)",M); if(M[1]) print M[1]}' $infofile)
	_type=${_type:-Function}
	read _type nil <<<${_type}
	Attr[type]=$_type

	#get test attr: level info
	_level=$(awk '{match($0,"(Type):[ \t]*.*([Tt]ier[0-9]+)",M); if(M[2]) print M[2]}' $infofile)
	_level=${_level:-Tier1}
	read _level nil <<<${_level}
	Attr[level]=$_level

	#get test setup: topologic if is multihost
	grep -q "Type:[[:space:]]*Multihost" $infofile &&
	topo=multiHost.1.1
	grep -q '\${\?CLIENTS}\?' $f && grep -q '\${\?SERVERS}\?' $f &&
	topo=multiHost.1.1
	##if specify the machine acount in $infofile
	nums=$(grep -i "Type:[[:space:]]*Multihost" $infofile | grep -o '[0-9]\+')
	{ read ServerNeed; read ClientNeed; } <<< "$nums"
	test -n "$ServerNeed" -o -n "$ClientNeed" &&
	topo=multiHost.${ServerNeed:-1}.${ClientNeed:-1}
	##if specify the machine acount in runtest.sh
	egrep -q '^[[:space:]]*(Client|Server)Need=' $f && {
		eval "$(egrep '^[[:space:]]*(Client|Server)Need=' $f)"
		topo=multiHost.${ServerNeed:-1}.${ClientNeed:-1}
	}
	[[ -n "$topo" ]] && Setup+=(--topo=$topo)

	# remove infofile
	rm -rf $infofile

	subtests_generator="gen_subtest.sh"
	[ -x $subtests_generator ] && sh $subtests_generator
	subtests_yml=$(egrep -h '^[[:space:]]*(attr|param|setup):' subtest.yml 2>/dev/null)

	if [[ "$FORMAT" = raw ]] || [[ -z "$subtests_yml" ]]; then
		_attr=$(echo -n "attr: {"; for e in "${!Attr[@]}"; do ymlhash "$e" "${Attr[$e]}"; done; echo -n "}")
		_attr=${_attr//, \}/\}}
		[[ ${#Setup[@]} > 0 ]] && {
			_setup=", "$(echo -n "setup: ["; for e in "${Setup[@]}"; do ymllist "$e"; done; echo -n "]")
			_setup=${_setup/, ]/]}
		}
		echo "- ${name}: {${_attr}${_setup}}"
	else
		while read l; do
			[[ -z "$l" ]] && continue

			unset Attr2 Setup2
			declare -A Attr2
			Setup2=()

			#update attr
			for key in "${!Attr[@]}"; do
				Attr2[$key]=${Attr[$key]}
			done
			while read k; do
				v=$(yaml2dict "$l" attr $k)
				[[ $k = distroin && $v = "" ]] && v=NULL
				[[ $k = disable && ($v = yes || $v = 1) ]] && continue 2
				Attr2[$k]=$v
			done < <(yaml2dict "$l" -keys attr)

			_attr= _param= _setup=
			# output as yaml format
			_attr=$(echo -n "attr: {"; for e in "${!Attr2[@]}"; do ymlhash "$e" "${Attr2[$e]}"; done; echo -n "}")
			_attr=${_attr//, \}/\}}

			#get the param list
			_param=$(echo "$l" | sed -rn '/param: /{s/.*(param: \[[^]]+]).*/\1/;p}')
			[[ -n "$_param" ]] && _param=", "$_param

			#get the setup list
			[[ -n "${Setup[*]}" ]] && Setup2=("${Setup[@]}")
			while read s; do
				if [[ "$s" =~ topo= && "${Setup[*]}" =~ topo= ]]; then
					for i in ${!Setup2[@]}; do
						[[ "${Setup2[$i]}" =~ topo= ]] && Setup2[$i]=$s
					done
				else
					Setup2+=("$s")
				fi
			done < <(yaml2dict "$l" setup)
			[[ ${#Setup2[@]} > 0 ]] && {
				_setup=", "$(echo -n "setup: ["; for e in "${Setup2[@]}"; do ymllist "$e"; done; echo -n "]")
				_setup=${_setup/, ]/]}
			}

			echo "- ${name}: {${_attr}${_param}${_setup}}"
		done <<<"$subtests_yml"
	fi

	popd >/dev/null
}

_casesInfo() {
	[ $# -lt 1 ] && {
		echo "Usage: func <case_path [case_path...]>" >&2
		return 1
	}
	for p in "$@"; do _caseInfo "$p"; done
}

_getTestList() {
	local paths= info=

	paths=$(find "$@" -name runtest.sh|sed -e 's;^.//*;;' -e 's;/runtest.sh$;;' -e 's/^runtest.sh$/./')
	for d in $paths; do
		[ -f ${d}/Makefile ] || continue

		info=$(_caseInfo ${d})
		[ -z "$info" ] && continue

		echo "$info"
	done
}

# __main__
#===============================================================================
export LANG=C
P=${0##*/}
FORMAT=std
#-------------------------------------------------------------------------------
Usage() {
	echo "Usage: $P [--raw | -e[expand_file] | -t <maxtime>] [\$dir ...]"
}
_at=`getopt -o hrt:e:: \
	--long help \
	--long raw \
	--long fmt: \
    -n "$P" -- "$@"`
eval set -- "$_at"

while true; do
	case "$1" in
	-h|--help)      Usage; shift 1; exit 0;;
	-r|--raw)       FORMAT=raw; shift 1;;
	--fmt)          FORMAT=$2; shift 2;;
	-t)		maxtime=$2; shift 2;;
	-e)		E=yes; setupConf=$2; shift 2;;
	--) shift; break;;
	esac
done

if [[ $E != yes ]]; then
	_getTestList "$@"

else
	testList=$(_getTestList "$@")
	testListWithSetup=$(echo "$testList"|grep 'setup: \[')
	testListWithOutSetup=$(echo "$testList"|grep -v 'setup: \[')

	[ -z "$setupConf" ] && {
		setupConf=/etc/bkr-client-improved/bkr.recipe.matrix.conf
		setupConfPrivate=~/.bkr-client-improved/bkr.recipe.matrix.conf
		[ -f $setupConfPrivate ] && setupConf=$setupConfPrivate
	}
	[ -r $setupConf ] || {
		echo "[Warn] can not read '$setupConf'" >&2
		exit 1
	}
	setupList=$(egrep -v '^ *#|^$' $setupConf)
	setupyList=$(while read setup; do echo $(eval set -- $setup; for a; do ymllist "--$a"; done); done <<<"$setupList")

	while read setup; do
		setup=${setup//;/\\;}
		echo "$testListWithSetup" | sed "s;] *}$;, ${setup%,}&;"
		echo "$testListWithOutSetup" | sed "s;}$;, setup: [${setup%,}]&;"
	done <<<"$setupyList"
fi
